package com.zh.webcommon.common.exception;

import com.zh.webcommon.common.exception.errorcode.BizError;
import com.zh.webcommon.common.viewdata.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import static com.zh.webcommon.common.exception.errorcode.BizError.DEFAULT_ERROR;
import static com.zh.webcommon.common.support.MessageUtils.message;

/**
 * 全局异常处理方式一：
 * 自定义一个异常控制器类
 * 使用@ControllerAdvice来自定义,(已经包含了@Component)
 * <p>
 * 此类的作用：
 * 将所有的@ExceptionHandler方法收集到一个类中，集中处理
 * 也可以在每个Controller中写 处理自定义异常的方法
 * <p>
 * 注意：异常先走AOP，再走全局异常
 */
@Slf4j
@ControllerAdvice
public final class MyGlobalExceptionHandler {

    public MyGlobalExceptionHandler() {
        log.info(" [MyGlobalExceptionHandler] constructor ...");
    }

    /**
     * 参数校验错误时 400
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({IllegalArgumentException.class, BindException.class})
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResult<Void> validFail(Exception ex) {
        log.warn("全局异常处理[{}].... {}", ex.getClass().getSimpleName(), ex.getMessage());

        if (ex instanceof BindException bindEx) {
            StringBuilder message = new StringBuilder();
            bindEx.getAllErrors().forEach(e -> {
                message.append(e.getDefaultMessage()).append("; ");
            });
            return ApiResult.<Void>fail(message.toString()).setError("");
        } else {
            return ApiResult.<Void>fail(message(ex.getMessage())).setError(ex.getMessage());
        }
    }

    /**
     * 没有认证时 401
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NotAuthorizeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ApiResult<Void> notAuthorize(NotAuthorizeException ex) {
        log.warn("全局异常处理[{}].... {}", ex.getClass().getSimpleName(), ex.getMessage());

        return ApiResult.<Void>fail(message("not.authorize")).setError(ex.getMessage());
    }

    /**
     * 没有权限时 403
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NotPermissionException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ApiResult<Void> notPermission(NotPermissionException ex) {
        log.warn("全局异常处理[{}].... {}", ex.getClass().getSimpleName(), ex.getMessage());

        return ApiResult.<Void>fail(message("not.permission")).setError(ex.getMessage());
    }

    /**
     * 非法操作时 406
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(OperationDeniedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
    public ApiResult<Void> illegalOperate(OperationDeniedException ex) {
        log.warn("全局异常处理[{}].... {}", ex.getClass().getSimpleName(), ex.getMessage());

        return ApiResult.<Void>fail(message("denied.operation")).setError(ex.getMessage());
    }


    /**
     * 找不到数据时 404
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NotFoundException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiResult<Void> notFound(NotFoundException ex) {
        log.warn("全局异常处理[{}].... {}", ex.getClass().getSimpleName(), ex.getMessage());

        return ApiResult.<Void>fail(message("not.found.data")).setError(ex.getMessage());
    }

    /**
     * 业务逻辑异常，
     * 返回http status 200。这是可预知的异常，一般无需处理
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ApiResult<Void> serviceFail(ServiceException ex) {
        log.error("全局异常处理[ServiceException].... {} ❌ {}", ex.bizError.message(), ex.getMessage());
        return ApiResult.<Void>fail(ex.bizError.code(), message(ex.bizError.message())).setError(ex.getMessage());
    }

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiResult<Void> runtimeFail(RuntimeException ex) {
        log.error("全局异常处理[RuntimeException].... {}", ex.getMessage());
        ex.printStackTrace();

        return ApiResult.<Void>fail(message(DEFAULT_ERROR.message())).setError(ex.getMessage());
    }

    public static void throwBizError(BizError bizError) {
        throw new ServiceException(bizError, "");
    }

}
